#include <JXQCore/JXQGlobal>
#if defined(J_OS_UNIX) || defined(J_OS_LINUX)

#include "jthread.h"
#include "jthread_p.h"
#include <cstring>

#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>

namespace JXQ
{

void *JThreadPrivate::run(void *arg)
{
	auto d_ptr = J_RCT(JThreadPrivate*, arg);

	d_ptr->q_ptr->run();

	int res = pthread_detach(d_ptr->m_tid);
	if( res < 0 )
		jError() << "JThread: pthread_detach: " << strerror(res);

	d_ptr->m_tid = 0;
	d_ptr->finish(); //!!!!!!!!!!!!
	return nullptr;
}

JThread::~JThread()
{
	if( d_ptr->m_tid > 0 and not d_ptr->m_isDetach )
		jError() << "## Warning: JThread: destruct before a thread terminates. call detach() to mask warning.";

	if( not --d_ptr->m_ref )
		delete d_ptr;
}

void JThread::start() noexcept
{
	if( d_ptr->m_tid > 0 )
		return;

	++d_ptr->m_ref;
	pthread_t tid;
	int res = pthread_create(&tid, nullptr, &JThreadPrivate::run, d_ptr);

	if( res < 0 )
		jError() << "JThread: pthread_create: " << strerror(res);
	else
		d_ptr->m_tid = tid;
}

bool JThread::isFinished() const
{
	return d_ptr->m_tid == 0;
}

JTid JThread::selfID()
{
	return pthread_self();
}

void JThread::usleep(uint32_t usecs)
{
	struct timeval t;
	t.tv_sec = usecs / 1000000;
	t.tv_usec = usecs - t.tv_sec * 1000000LL;
	select(0, nullptr, nullptr, nullptr, &t);
}

void JThread::msleep(uint32_t msecs)
{
	struct timeval t;
	t.tv_sec = msecs / 1000;
	t.tv_usec = 1000 * (msecs - t.tv_sec*1000);
	select(0, nullptr, nullptr, nullptr, &t);
}

void JThread::sleep(uint32_t secs)
{
	struct timeval t;
	t.tv_sec = secs;
	t.tv_usec = 0;
	select(0, nullptr, nullptr, nullptr, &t);
}

void JThread::sleep(uint32_t secs, uint32_t msecs)
{
	struct timeval t;
	int32_t s = msecs / 1000;
	t.tv_sec = secs + s;
	t.tv_usec = (msecs - s * 1000) * 1000;
	select(0, nullptr, nullptr, nullptr, &t);
}

void JThread::sleep(uint32_t secs, uint32_t msecs, uint32_t usecs)
{
	struct timeval t;
	int32_t s1 = msecs / 1000;
	int32_t s2 = usecs / 1000000;
	t.tv_sec = secs + s1 + s2;
	t.tv_usec = (msecs - s1 * 1000) * 1000 + secs - s2 * 1000000LL;
	select(0, nullptr, nullptr, nullptr, &t);
}

bool JThread::termination()
{
	int res = pthread_cancel(d_ptr->m_tid);
	if( res != 0 )
		jError() << "JThread::termination: call failed:" << strerror(res);
	return res == 0;
}

JXQLIB_EXPORT int JThread::getCPUCoreCount()
{
#if 0
	cpu_set_t cs;
	CPU_ZERO(&cs);
	sched_getaffinity(0, sizeof(cs), &cs);

	int count = 0;
	for(int i=0; i<8; i++)
	{
		if( CPU_ISSET(i, &cs) )
			count++;
	}
	return count;
#else
	return J_SCT(int, sysconf(_SC_NPROCESSORS_ONLN));
#endif
}

} //namespace JXQ


#endif //unix/linux
